// 类型守卫

// 1、类型收缩 | 类型收窄
// typeof  不能区分数组、函数、对象、null 都会返回"object"
const isString = (str: any) => typeof str === 'string';

const isArray = (arr: any) => Array.isArray(arr);
const isArray2 = (arr: any) => arr instanceof Array;

// 2、类型谓词 自定义守卫

// 实现一个函数，该函数可以传入任何类型
// 如果传入的是object，就检查里边的属性，如果属性是number，就取两位小数
// 如果是string，就去除左右空格
// 如果是函数，就执行

// Object.prototype 可以简写成 ({})
const isObj = (obj: any) => Object.prototype.toString.call(obj) === '[object Object]';
// const isNum = (num: any) => typeof num === 'number';
// 自定义守卫写法 1、返回一个boolean值 2、语法规则：参数 is 类型  （如果返回true，则参数就是想要的类型）
// 判断通过后类型自动就变成了判断后的类型
const isNum = (num: any): num is number => typeof num === 'number';
const isStr = (str: any) => typeof str === 'string';
const isFn = (fn: any) => typeof fn === 'function';
const fn = (data: any) => {
    if(isObj(data)) {
        Object.keys(data).forEach(key => {
            const val = data[key]
            if(isNum(val)) {
                data[key] = val.toFixed(2)
            }
            if (isStr(val)) {
                data[key] = val.trim()
            }
            if (isFn(val)) {
                // val()  这样调用函数里边的this 会指向window
                data[key]() // 这样通过对象自身调用this 会指向对象本身
            }
        })
    }
}

const obj = {
    name: ' 张三   ',
    age: 18,
    sex: '  男',
    fn: function () {
        console.log(this.name);
    }
}
fn(obj)
console.log(obj)


interface A {
    name: string;
    age: number;
}

interface B {
    name: string;
    age: number;
    sex: string;
}

let a: A = {
    name: '张三',
    age: 18
}
let b: B = {
    name: '张三hah',
    age: 20,
    sex: '男'
}
// 类型斜变
// b = a; // 赋值会报错 相当于A为主类型，B为子类型，B完全能覆盖A，所以B能赋给A  类型斜变
a = b; // 赋值成功  赋值规则，只能多不能少，多的给少的赋值
console.log(a)

let fna = (a: A) =>{

}
let fnb = (b: B) => {

}
// 类型逆变
// fna = fnb;  // 会报错
fnb = fna;  // 赋值成功  此时执行fnb,其实是执行的fna函数


// 泛型工具

// Partial 所有属性可选
// Required 所有属性必选
// Pick 挑选某些属性
// Omit 排除某些属性，并返回新的类型
// Exclude 排除某些属性

interface Person {
    name: string;
    age: number;
    sex: string;
}

type PersonPartial = Partial<Person>;

// 自定义实现
type CustomPartial<T> = {
    [P in keyof T]?: T[P];
}

interface Person2 {
    name?: string;
    age?: number;
    sex: string;
}
type RequiredPerson = Required<Person2>;

// 自定义实现
type CustomRequired<T> = {
    [P in keyof T]-?: T[P];
}
type cusPerson = CustomRequired<Person2>;

// Pick

type PickPerson = Pick<Person2, 'name' | 'sex'>;

type CustomPick<T, K extends keyof T> = {
    [P in K]: T[P];
}

type cusPickPerson = CustomPick<Person2, 'name' | 'sex'>;

// Omit
type OmitPerson = Omit<Person2, 'name'>;

type CustomOmit<T, K extends keyof T> = {
    [P in Exclude<keyof T, K>]: T[P];
}

type CustomOmit2<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

type cusOmitPerson = CustomOmit<Person2, 'name'>;

// Exclude

// Exclude 接收两个联合类型，返回第一个联合类型中有，第二个联合类型中没有的元素
type ExcludePerson = Exclude< string | number | boolean, number>;

// 自定义Exclude
type CustomExclude<T, K> = T extends K ? never : T;

type person = CustomExclude< string | number | boolean, number>;



// Record  ReturnType

// 1、Record 约束对象的key和value  接收两个泛型，第一个参数是key的类型，第二个参数是value的类型
type Keys = 'name' | 'age' | 'sex'; // key必须全部包含
type Value = '唱歌' | '跳舞' | ' coding'; // value不用全部包含

type costom = keyof any // string | number | symbol 的语法糖  keyof any
const person: Record<Keys, Value> = {
    name: '唱歌',
    age: '跳舞',
    sex: '唱歌'
};

// ReturnType<T>  获取函数返回值类型
const fn3 = (a: number, b: number) => {
  return [a, b, 'fdfd'];
};
type FnReturnType = ReturnType<typeof fn3>;
